﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Engine
{
	/// <summary>
	/// Component that can be made of other components.
	/// 
	/// Most important here is the Connect() method which allows binding the OutPort of one
	/// Component to the InPort of another.
	/// </summary>
	public abstract class CompositeComponent: UserCodedComponent
	{
		private HashSet<IComponent> components;

		public CompositeComponent()
			: base()
		{
			this.components = new HashSet<IComponent>();
		}

        /// <summary>
        /// Connects two Ports together
        /// </summary>
        /// <typeparam name="T">the data type carried by the Connection</typeparam>
        /// <param name="headPort">Port supplying data to the Connection</param>
        /// <param name="tailPort">Port receiving data from the Connection</param>
        private void ConnectHelper<T>(Port<T> headPort, Port<T> tailPort)
        {
            Connection<T> link = new Connection<T>(headPort, tailPort);
            headPort.ConnectOutgoing(link);
            tailPort.ConnectIncoming(link);
        }

        /// <summary>
        /// Connects an external InPort from a CompositeComponent to the internal InPort
        /// of one of its contained Components
        /// </summary>
        /// <typeparam name="T">the type of the Connection</typeparam>
        /// <param name="externalInport">InPort supplying data to the Connection</param>
        /// <param name="internalInport">InPort receiving data from the Connection</param>
        public void Connect<T>(InPort<T> externalInport, InPort<T> internalInport)
        {
			this.ConnectHelper<T>(externalInport, internalInport);
        }

        /// <summary>
        /// Connects an internal OutPort on a Component contained within a CompositeComponent
        /// to an external OutPort on the CompositeComponent
        /// </summary>
        /// <typeparam name="T">the type of the Connection</typeparam>
        /// <param name="internalOutport">OutPort supplying data to the Connection</param>
        /// <param name="externalOutport">OutPort receiving data from the Connection</param>
        public void Connect<T>(OutPort<T> internalOutport, OutPort<T> externalOutport)
        {
			this.ConnectHelper<T>(internalOutport, externalOutport);
        }

        /// <summary>
        /// Standard OutPort-to-InPort Connection.
        /// </summary>
        /// <typeparam name="T">the type of the Connection</typeparam>
        /// <param name="headPort">OutPort supplying data to the Connection</param>
        /// <param name="tailPort">InPort receiving data from the Connection</param>
        public void Connect<T>(OutPort<T> headPort, InPort<T> tailPort)
        {
			this.ConnectHelper<T>(headPort, tailPort);
        }
	}
}
